package com.cloudbroker.bcs.common.configure;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cloudbroker.bcs.common.configure.constants.ConfigureConstants;
import com.cloudbroker.bcs.common.context.RuntimeContext;
import com.cloudbroker.bcs.common.zookeeper.PathChildrenCacheEventListener;
import com.cloudbroker.bcs.common.zookeeper.ZooKeeperClient;
import com.cloudbroker.bcs.common.zookeeper.factory.HSBCSZooKeeperClientFactory;

public class HSBCSZooKeeperConfigLoader {
    
    private static final Logger LOG = LoggerFactory.getLogger(HSBCSZooKeeperConfigLoader.class);
    
    private HSBCSZooKeeperConfigurator zkConfigurator;
    
    // 所有配置根路径
    private String configRootPath;
    
    // 所有需要发现租户的类别定义（以这些类别下的子路径发现租户为准）
    private Set<String> needListenForTenantsCategorys = new HashSet<String>();
    
    private String needListenForTenantsCategory;
    
    // 对每个类别的监听
    private Map<String, HSBCSConfigZooKeeperWatcher> inCategoryWatchers = new HashMap<String, HSBCSConfigZooKeeperWatcher>();
    
    // 增减类别监听的锁
    private Lock watchersLock = new ReentrantLock();
    
    // 可设置感兴趣的类别
    private Set<String> interestedCategories;
    
    /**
     * 作为ServletContextListener来载入zookeeper配置，主要用于没有spring的环境
     * @author zhaohc
     *
     */
    // 写成内部子类而不是直接在主类上实现ServletContextListener接口，避免在没有ServletContextListener相关jar的环境下无法载入主类
    public static class ServletContextListenerLoader implements ServletContextListener {
        
        private static HSBCSZooKeeperConfigLoader configLoader;
        
        @Override
        public void contextInitialized(ServletContextEvent sce) {
            // 构造loader主类
            configLoader = new HSBCSZooKeeperConfigLoader();
            // 以无spring环境初始化
            configLoader.doInit(false);
            // 将zkConfigurator保存到ServletContext以供其他类（如servlet或listener）使用
            sce.getServletContext().setAttribute(HSBCSZooKeeperConfigurator.class.getName(),
                    configLoader.getZkConfigurator(false));
        }
        
        @Override
        public void contextDestroyed(ServletContextEvent sce) {
            // 关闭servletContext时销毁loader
            configLoader.doDestory();
        }
        
    }
    
    @PostConstruct
    public void init() {
        // 该方法应该被spring调用，以spring环境初始化
        doInit(true);
    }
    
    @PreDestroy
    public void destroy() {
        doDestory();
    }
    
    private void doInit(boolean initBySpring) {
        LOG.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>init HSBCSZooKeeperConfigLoader");
        
        // 获取配置根路径
        configRootPath = HSBCSZooKeeperConfigurator.getConfigRootPath();
        
        // 初始化定义所有需要发现租户的类别（以这些类别下的子路径发现租户为准）
        needListenForTenantsCategorys.add(ConfigureConstants.CATEGORY_TENANT);
        needListenForTenantsCategorys.add("auth");
        
        needListenForTenantsCategory = needListenForTenantsCategorys.iterator().next();
        
        // 以相应环境标志获取zkConfigurator
        getZkConfigurator(initBySpring);
        // 如果无法获取zkConfigurator则抛出异常，初始化失败
        if (null == zkConfigurator) {
            LOG.warn("Can not get ZooKeeperConfigurator, init ZooKeeperConfigLoader failed!!!");
            return;
        }
        
        // 设置PropertiesUtils运行方式
        ConfigUtil.setZkConfigurator(zkConfigurator);
        
        // 注册配置类别监听器
        // 先确认该路径存在，不存在先创建，不然监听不到
        zkConfigurator.getClient().watchPathChildren(configRootPath, new ConfigCategoryListener());
    }
    
    private void doDestory() {
        if (null != zkConfigurator) {
            // 注销配置类别监听
            zkConfigurator.getClient().unregisterPathChildrenWatch(configRootPath);
            // 销毁所有类别上的监听
            watchersLock.lock();
            try {
                Iterator<Entry<String, HSBCSConfigZooKeeperWatcher>> entriesIt = inCategoryWatchers.entrySet()
                        .iterator();
                while (entriesIt.hasNext()) {
                    Entry<String, HSBCSConfigZooKeeperWatcher> entry = entriesIt.next();
                    // 销毁监听
                    entry.getValue().dispose();
                    // 移除监听
                    entriesIt.remove();
                }
            } finally {
                watchersLock.unlock();
            }
        }
    }
    
    private HSBCSZooKeeperConfigurator getZkConfigurator(boolean initBySpring) {
        if (null == zkConfigurator) {
            ZooKeeperClient zkClient = null;
            // 如果由spring初始化并调用，从spring中获取，否则自己调用工厂创建
            if (initBySpring) {
                zkClient = RuntimeContext.getBean(ZooKeeperClient.class);
            } else {
                zkClient = HSBCSZooKeeperClientFactory.createZooKeeperClient();
            }
            // 如果可以获得ZooKeeperClient，则创建HSBCSZooKeeperConfigurator
            if (null != zkClient) {
                zkConfigurator = new HSBCSZooKeeperConfigurator(zkClient);
            }
        }
        return zkConfigurator;
    }
    
    public HSBCSZooKeeperConfigurator getZkConfigurator() {
        return zkConfigurator;
    }
    
    private String extractCategoryFromFullPath(String fullPath) {
        // 剥取category：/configRootPath/category
        if (fullPath.startsWith(configRootPath) && fullPath.length() > configRootPath.length() + 1) {
            return fullPath.substring(configRootPath.length() + 1);
        }
        return null;
    }
    
    private boolean isOmitCategory(String category) {
        // interestedCategories空不检查
        return !(null == interestedCategories || interestedCategories.isEmpty())
                && !interestedCategories.contains(category);
    }
    
    private boolean needListenForTenants(String category) {
        // 检查是否是定义的监听租户增减的类别
        return needListenForTenantsCategorys.contains(category);
    }
    
    private void addInCategoryWatcher(String category) {
        // 如果不感兴趣，则不添加该类别
        if (isOmitCategory(category)) {
            return;
        }
        boolean added = false;
        HSBCSConfigZooKeeperWatcher watcher = inCategoryWatchers.get(category);
        if (null == watcher) {
            watchersLock.lock();
            try {
                watcher = inCategoryWatchers.get(category);
                if (null == watcher) {
                    // 根据是否需要监听租户增减创建监听器
                    if (needListenForTenants(category)) {
                        watcher = new HSBCSConfigZooKeeperWatcher(zkConfigurator, category, true, this);
                    } else {
                        watcher = new HSBCSConfigZooKeeperWatcher(zkConfigurator, category, false, null);
                    }
                    // 获取监听租户增减的监听器（定义该类监听器可能有很多，但只需要找到一个就可以做到不遗漏租户）
                    HSBCSConfigZooKeeperWatcher tenantsWatcher = inCategoryWatchers
                            .get(needListenForTenantsCategory);
                    if (null != tenantsWatcher) {
                        // 如果有监听租户增减的监听器，获取其已知的所有租户，注册到本类型监听器上
                        Set<String> tenantIds = tenantsWatcher.getTenantIds();
                        watcher.registerTenants(tenantIds);
                    }
                    inCategoryWatchers.put(category, watcher);
                    added = true;
                }
            } finally {
                watchersLock.unlock();
            }
        }
        // 打日志放到锁之外，尽量减少在锁中耗时和可能由于输出被hang住的情况
        if (added) {
            LOG.info("config category " + category + " added");
        }
    }
    
    private void removeInCategoryWatcher(String category) {
        boolean removed = false;
        watchersLock.lock();
        try {
            removed = null != inCategoryWatchers.remove(category);
        } finally {
            watchersLock.unlock();
        }
        // 打日志放到锁之外，尽量减少在锁中耗时和可能由于输出被hang住的情况
        if (removed) {
            LOG.info("config category " + category + " removed");
        }
    }
    
    public void registerTenant(String tenantId) {
        watchersLock.lock();
        try {
            // 通知所有类别的监听器，重复通知调用该方法的类别的监听器也没关系，方法内已保证重入无害，不会重复添加或无限递归
            for (HSBCSConfigZooKeeperWatcher watcher : inCategoryWatchers.values()) {
                watcher.registerTenant(tenantId);
            }
            // 获取对应的TenantConfig
            ConfigUtil.getTenantConfig(tenantId, false);
        } finally {
            watchersLock.unlock();
        }
    }
    
    public void unregisterTenant(String tenantId) {
        watchersLock.lock();
        try {
            // 通知所有类别的监听器，重复通知调用该方法的类别的监听器也没关系，方法内已保证重入无害，不会重复删除或无限递归
            for (HSBCSConfigZooKeeperWatcher watcher : inCategoryWatchers.values()) {
                watcher.unregisterTenant(tenantId);
            }
            // 删除对应的TenantConfig
            ConfigUtil.removeTenantConfig(tenantId);
        } finally {
            watchersLock.unlock();
        }
    }
    
    private class ConfigCategoryListener implements PathChildrenCacheEventListener {
        
        @Override
        public void childPathAdded(String fullPath, byte[] data, Stat stat) {
            // 提取category
            String category = extractCategoryFromFullPath(fullPath);
            if (null != category) {
                // 如果可以提取出category，添加该类别的监听器
                addInCategoryWatcher(category);
            }
        }
        
        @Override
        public void childPathUpdated(String fullPath, byte[] data, Stat stat) {
            // 委托给add方法
            childPathAdded(fullPath, data, stat);
        }
        
        @Override
        public void childPathRemoved(String fullPath, Stat stat) {
            // 提取category
            String category = extractCategoryFromFullPath(fullPath);
            if (null != category) {
                // 如果可以提取出category，删除该类别的监听器
                removeInCategoryWatcher(category);
            }
        }
        
        @Override
        public boolean isNeedRecursionWatch() {
            // 所有配置根路径下只有一层配置类别，无需递归监听
            return false;
        }
        
        @Override
        public String toString() {
            return getClass().getName();
        }
    }
    
    public Set<String> getInterestedCategories() {
        return interestedCategories;
    }
    
    public void setInterestedCategories(Set<String> interestedCategories) {
        this.interestedCategories = interestedCategories;
    }
    
    public Set<String> getNeedListenForTenantsCategorys() {
        return needListenForTenantsCategorys;
    }
    
    public void setNeedListenForTenantsCategorys(Set<String> needListenForTenantsCategorys) {
        this.needListenForTenantsCategorys = needListenForTenantsCategorys;
    }
    
}
